/*
 *  Disc Catalog
 *  A little application to quickly catalog and find contents of your
 *  DVD archives.
 *
 *  File.m
 *  The class representing the leaf and the nodes in the model's tree.
 *
 *
 *  Created by Nicola Gigante on 18/12/08.
 *  Copyright (C) 2008  Nicola Gigante
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#import "File.h"


@implementation File

@synthesize files, visibleFiles, name, type, typeString, size, icon, notes, isPackage, isHidden, root;

+ (void) initialize
{
	[self setKeys:[NSArray arrayWithObject:@"files"] triggerChangeNotificationsForDependentKey:@"filteredFiles"];
}

+ (NSImage *) folderIcon
{
	static NSImage *icon = nil;
	if(icon == nil)
		icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)];
	return icon;
}

- (id) initWithCoder:(NSCoder *)decoder
{
	self = [super init];
	
	self.files = [decoder decodeObjectForKey:@"files"];
	if(self.files)
		self.visibleFiles = [NSMutableArray arrayWithArray: [self getVisibleFiles:self.files]];
	self.name = [decoder decodeObjectForKey:@"name"];
	self.type = [decoder decodeObjectForKey:@"type"];
	self.size = [decoder decodeInt64ForKey:@"size"];
	self.isPackage = [decoder decodeBoolForKey:@"isPackage"];
	self.isHidden = [decoder decodeBoolForKey:@"isHidden"];
	self.icon = [[NSImage alloc] initWithData: [decoder decodeObjectForKey:@"icon"]];
	self.notes = [decoder decodeObjectForKey:@"notes"];
	
	if(!self.icon)
	{
		NSString *fileType  = self.type;
		if([self isDirectory])
			fileType = NSFileTypeForHFSTypeCode(kGenericFolderIcon);
		
		self.icon = [[NSWorkspace sharedWorkspace] iconForFileType:fileType];
	}
	
	return self;
}

- (void) encodeWithCoder:(NSCoder *)encoder
{
	[encoder encodeObject:self.files forKey:@"files"];
	[encoder encodeObject:self.name forKey:@"name"];
	[encoder encodeObject:self.type forKey:@"type"];
	[encoder encodeInt64:self.size forKey:@"size"];
	[encoder encodeBool:self.isPackage forKey:@"isPackage"];
	[encoder encodeBool:self.isHidden forKey:@"isHidden"];
	[encoder encodeObject:([self isPackage] ? [self.icon TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:0] : nil) forKey:@"icon"];
	[encoder encodeObject:self.notes forKey:@"notes"];
}

- (id) initWithFile:(File *)file andChildren:(BOOL) children
{
	self = [super init];
	
	self.name = file.name;
	self.size = file.size;
	if(children)
		self.files = [file.files copy];
	else
		self.files = [file isDirectory] ? [NSMutableArray array] : nil;
	visibleFiles = [NSMutableArray arrayWithArray:[self getVisibleFiles:self.files]];
	self.type = file.type;
	self.notes = file.notes;
	self.isPackage = file.isPackage;
	self.isHidden = file.isHidden;
	self.icon = file.icon;
	
	return self;
}

- (id) initFileWithName:(NSString *)_name andSize:(unsigned long long)_size
{
	self = [super init];
	self.name = _name;
	self.size = _size;
	self.files = nil;
	self.visibleFiles = nil;
	self.type = @"";
	self.notes = @"";
	self.isPackage = NO;
	self.isHidden = NO;
	showHiddenFiles = [[NSUserDefaults standardUserDefaults] boolForKey:@"showHiddenFiles"];
	
	return self;
}

- (id) initDirectoryWithName:(NSString *)_name
{
	self = [super init];
	self.name = _name;
	self.size = 0;
	self.files = [NSMutableArray array];
	self.visibleFiles = [NSMutableArray array];
	self.type = @"";
	self.icon = [File folderIcon];
	self.notes = @"";
	self.isPackage = NO;
	self.isHidden = NO;
	showHiddenFiles = [[NSUserDefaults standardUserDefaults] boolForKey:@"showHiddenFiles"];
	
	return self;
}

- (void) setRoot: (File *)r
{
	for(File *f in self.files)
		f.root = r;
	root = r;
}

+ (File *) scanFromPath:(NSString *)path
{
	NSFileManager *manager = [NSFileManager defaultManager];
	
	File *file;
	BOOL isDirectory = FALSE;
	[manager fileExistsAtPath:path isDirectory:&isDirectory];
	if(isDirectory)
	{
		file = [[File alloc] initDirectoryWithName:[manager displayNameAtPath:path]];
		file.isPackage = [[NSWorkspace sharedWorkspace] isFilePackageAtPath:path];
		if(file.isPackage)
		{
			NSImage *ws_icon = [[NSWorkspace sharedWorkspace] iconForFile:path];
			file.icon = [[NSImage alloc] initWithSize:(NSSize){32,32}];
			[file.icon lockFocus];
			[ws_icon drawInRect:(NSRect){0,0,32,32} fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
			[file.icon unlockFocus];
		}else
			file.icon = [File folderIcon];
		
		NSString *type;
		[[NSWorkspace sharedWorkspace] getInfoForFile:path application:NULL type:&type];
		file.type = type;
	}
	else
	{
		unsigned long long size = [[[manager attributesOfItemAtPath:path error:NULL] objectForKey:NSFileSize] longLongValue];
		file = [[File alloc] initFileWithName:[manager displayNameAtPath:path] andSize:size];
		NSString *type;
		[[NSWorkspace sharedWorkspace] getInfoForFile:path application:NULL type:&type];
		file.type = type;
		file.icon = [[NSWorkspace sharedWorkspace] iconForFileType:type];
	}
	file.isHidden = ([file.name characterAtIndex:0] == '.');
	file.typeString = [[NSWorkspace sharedWorkspace] localizedDescriptionForType:[[NSWorkspace sharedWorkspace] typeOfFile:path error:NULL]];
	
	NSArray *contents = [manager directoryContentsAtPath:path];
	for(NSString *subpath in contents)
	{
		File *subfile = [File scanFromPath:[path stringByAppendingPathComponent:subpath]];
		[file insertObject:subfile inFilesAtIndex:[file countOfFiles]];
	}
	
	return file;
}

- (BOOL) isFile
{
	return (files == nil) || (self.isPackage && [[NSUserDefaults standardUserDefaults] boolForKey:@"opaquePackages"]);
}

- (BOOL) isDirectory
{
	return (files != nil);
}

- (void) printWithIndentation:(int)ind
{
	NSString *inds = @"";
	for(int i = 0; i < ind; i++)
		inds = [inds stringByAppendingString:@"  "];
	NSLog(@"%@%@%s", inds, self.name, [self isFile] ? "" : "/");
	if([self isFile])
		return;
	for(File *f in self.files)
	{
		[f printWithIndentation:ind + 1];
	}
}

- (NSString *)formattedSize
{
	unsigned long long s = self.size;
	int count;
	NSString *unit = @"";
	
	for(count = 0; s >= 1024; count++)
		s /= 1024;
	
	switch(count)
	{
		case 0:
			unit = @"Byte";
			break;
		case 1:
			unit = @"KB";
			break;
		case 2:
			unit = @"MB";
			break;
		case 3:
			unit = @"GB";
			break;
	}
	
	NSString *str = [NSString stringWithFormat:@"%qu %@", s, unit];
	return str;
}

- (NSArray *) getVisibleFiles: (NSArray *)unfiltered
{
	if(unfiltered == nil)
		return nil;
	
	NSMutableArray *visible = [NSMutableArray array];
	
	for(File *f in unfiltered)
		if(![f isHidden])
			[visible addObject:f];
	
	return visible;
}

- (unsigned) countOfVisibleFiles
{
	if([[NSUserDefaults standardUserDefaults] boolForKey:@"showHiddenFiles"])
		return files.count;
	else
		return [visibleFiles count];
}

- (File *) objectInVisibleFilesAtIndex:(unsigned)index
{
	if([[NSUserDefaults standardUserDefaults] boolForKey:@"showHiddenFiles"])
		return [files objectAtIndex:index];
	else
		return [visibleFiles objectAtIndex:index];
}

- (NSMutableArray *) visibleFiles
{
	if([[NSUserDefaults standardUserDefaults] boolForKey:@"showHiddenFiles"])
		return files;
	else
		return visibleFiles;
}

- (unsigned) countOfFiles
{
	if(!files)
		return 0;
	return files.count;
}

- (File *) objectInFilesAtIndex:(unsigned)index
{
	return [files objectAtIndex:index];
}

- (void) removeObjectFromFilesAtIndex:(int)index
{
	File *file = [files objectAtIndex:index];
	self.size -= file.size;
	[files removeObjectAtIndex:index];
	if(![file isHidden])
		[visibleFiles removeObject:file];
}

- (void) insertObject:(File *)file inFilesAtIndex:(int)index
{
	if(!files || !visibleFiles)
	{
		files = [NSMutableArray array];
		visibleFiles = [NSMutableArray array];
		self.size = 0;
	}
	[files insertObject:file atIndex:index];
	self.size += file.size;
	if(![file isHidden])
		[visibleFiles addObject:file];
}

@end
